#include "Geometry.h"

#include <vector>
#include <list>

namespace physicsworks {

// to relate the normal with its respective face
struct NormalFaceRef {
	vec3 normal;
	std::list<int> faces;
	
	bool operator < (const NormalFaceRef& v) const {
		return normal < v.normal;
	}
};

Geometry::Geometry(vec3* vertices, int numVertices, const int* verticesIndices, int numFaces)
{
	this->vertices = vertices;
	this->numVertices = numVertices;
	this->faces = new FaceRef[numFaces];
	this->numFaces = numFaces;
	
	std::vector<NormalFaceRef> nfrs(numFaces);
	
	for (int i = 0; i < numFaces; ++i) {
		const vec3* v0 = this->faces[i].v0 = &vertices[ verticesIndices[3*i + 0] ];
		const vec3* v1 = this->faces[i].v1 = &vertices[ verticesIndices[3*i + 1] ];
		const vec3* v2 = this->faces[i].v2 = &vertices[ verticesIndices[3*i + 2] ];
		
		NormalFaceRef nfr;
		
		// calculate the normal
		nfr.normal = (*v1 - *v0).cross(*v2 - *v0);
		nfr.normal.normalize();
		nfr.faces.push_back(i);
		nfrs[i] = nfr;
	}
	
	// normals (and its faces) without duplication
	std::vector<NormalFaceRef> unfrs;
	unfrs.reserve(numFaces);
	
	// sort to get the adjacents equal normals
	std::sort(nfrs.begin(), nfrs.end());
	
	// get the union of faces from adjacents equal normals and insert into unfrs
	unsigned int i = 0;
	while ( i < nfrs.size() ) {
		unsigned int ii = i++;
		unfrs.push_back( nfrs[ii] );
		
		while (i < nfrs.size() && nfrs[i].normal == nfrs[ii].normal) {
			unfrs.back().faces.push_back( nfrs[i].faces.back() );
			++i;
		}
		
	}
	
	// generate the normals array and the normal reference of faces
	this->normals = new vec3[unfrs.size()];
	this->numNormals = unfrs.size();
	for (i = 0; i < unfrs.size(); ++i) {
		
		this->normals[i] = unfrs[i].normal;
		for (std::list<int>::const_iterator iterf = unfrs[i].faces.begin(); iterf != unfrs[i].faces.end(); ++iterf)
			this->faces[*iterf].normal = &this->normals[i];
	}
	// TODO: generate the EdgeRefs

}

/**
 * Returns the min and max vectors representing the x, y, z's min
 * and max values respectively.
 */
void FaceRef::minMaxVerts(vec3* min, vec3* max) const
{
	for (int i = 0; i < 3; ++i) {
		if ( (*v0)(i) > (*v1)(i) ) {
			if ( (*v0)(i) > (*v2)(i) ) {
				(*max)(i) = (*v0)(i);
				(*min)(i) = std::min( (*v1)(i), (*v2)(i) );
			} else {
				(*max)(i) = (*v2)(i);
				(*min)(i) = std::min( (*v0)(i), (*v1)(i) );
			}
		} else {
			if ( (*v1)(i) > (*v2)(i) ) {
				(*max)(i) = (*v1)(i);
				(*min)(i) = std::min( (*v0)(i), (*v2)(i) );
			} else {
				(*max)(i) = (*v2)(i);
				(*min)(i) = std::min( (*v0)(i), (*v1)(i) );
			}
		}
	}
}


}
